// 版权所有2010 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package cmplx

import "math"

// 下面的原始C代码、长注释和常量
// 来自http:
// go代码是原始C.
// 
// Cephes数学库发行版2.8:2000年6月
// 版权所有1984、1987、1989、1992、2000作者Stephen L.Moshier 
// 
// http:
// 本档案中的一些软件可能来自《数学函数的方法和程序》（Prentice Hall或Simon&Schuster 
// International，1989）一书，也可能来自Cephes数学图书馆，一本
// 商业产品。无论哪种情况，它都是作者的版权。
// 您在这里看到的内容可以免费使用，但不提供任何支持或保证。
// 
// 本书中两个已知的印刷错误在这里的
// 伽马函数和不完整的beta 
// 积分的源代码列表中修复。
// 
// Stephen L.Moshier 
// moshier@na-网。奥尔。gov 

// 复平方根
// 
// 说明：
// 
// 如果z=x+iy，r=|z |，那么
// 
// 1/2 
// 
// 
// 使用
// 标识2 Re w Im w=y可避免r-x或r+x中的取消错误。
// 
// 注意，-w也是z的平方根。选择的根
// 始终在右半平面上，且Im w与y具有相同的符号。
// 
// 算术域#试验峰值均方根
// 12月-10日，+1025000 3.2e-17 9.6e-18 
// IEEE-10，+101000000 2.9e-16 6.1e-17 

// Sqrt返回x的平方根。
// 选择结果r，使其为真（r）≥ 0和imag（r）的符号与imag（x）的符号相同。
func Sqrt(x complex128) complex128 {
	if imag(x) == 0 {
		// 确保imag（r）的符号与imag（x）的符号相同，因为imag（x）=符号为零。
		if real(x) == 0 {
			return complex(0, imag(x))
		}
		if real(x) < 0 {
			return complex(0, math.Copysign(math.Sqrt(-real(x)), imag(x)))
		}
		return complex(math.Sqrt(real(x)), imag(x))
	} else if math.IsInf(imag(x), 0) {
		return complex(math.Inf(1.0), imag(x))
	}
	if real(x) == 0 {
		if imag(x) < 0 {
			r := math.Sqrt(-0.5 * imag(x))
			return complex(r, -r)
		}
		r := math.Sqrt(0.5 * imag(x))
		return complex(r, r)
	}
	a := real(x)
	b := imag(x)
	var scale float64
	// 重新缩放以避免内部溢出或下溢。
	if math.Abs(a) > 4 || math.Abs(b) > 4 {
		a *= 0.25
		b *= 0.25
		scale = 2
	} else {
		a *= 1.8014398509481984e16 // 2**54 
		b *= 1.8014398509481984e16
		scale = 7.450580596923828125e-9 // 2**-27 
	}
	r := math.Hypot(a, b)
	var t float64
	if a > 0 {
		t = math.Sqrt(0.5*r + 0.5*a)
		r = scale * math.Abs((0.5*b)/t)
		t *= scale
	} else {
		r = math.Sqrt(0.5*r - 0.5*a)
		t = scale * math.Abs((0.5*b)/r)
		r *= scale
	}
	if b < 0 {
		return complex(t, -r)
	}
	return complex(t, r)
}
